<!doctype html>
<html lang="en-us">
<head>

    <meta charset="utf-8">
    <meta name="generator" content="Hugo 0.59.1" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>OAuth2.0认证授权单点登录 | 林贤钦</title>
    <meta property="og:title" content="OAuth2.0认证授权单点登录 - 林贤钦">
    <meta property="og:type" content="article">
        
    <meta property="article:published_time" content="2020-04-18T22:25:00&#43;08:00">
        
        
    <meta property="article:modified_time" content="2020-04-18T22:25:00&#43;08:00">
        
    <meta name="Keywords" content="java,博客,项目管理,软件架构,公众号,小程序">
    <meta name="description" content="OAuth2.0认证授权单点登录">
        
    <meta name="author" content="林贤钦">
    <meta property="og:url" content="http://linxianqin.gitee.io/post/OAuth2.0%E8%AE%A4%E8%AF%81%E6%8E%88%E6%9D%83%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95/">
    <link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon">

    <link rel="stylesheet" href="/css/normalize.css">
    
        <link rel="stylesheet" href="/css/prism.css">
    
    <link rel="stylesheet" href="/css/style.css">
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

    


    
    
</head>

<body>
<header id="header" class="clearfix">
	<div class="container">
        <div class="col-group">
            <div class="site-name ">
                
                    <a id="logo" href="http://linxianqin.gitee.io">
                        林贤钦
                    </a>
                
                <p class="description">专注于Java、微信小程序、移动互联网、项目管理、软件架构</p>
            </div>
            <div>
                <nav id="nav-menu" class="clearfix">
                    <a class="current" href="http://linxianqin.gitee.io">首页</a>
                    
                    <a  href="http://linxianqin.gitee.io/archives/" title="归档">归档</a>
                    
                    <a  href="http://linxianqin.gitee.io/about/" title="关于">关于</a>
                    
                </nav>
            </div>
        </div>
    </div>
</header>


<div id="body">
        
        
    <div class="container">
        <div class="col-group">

            <div class="col-8" id="main">
                <div class="res-cons">
                    <article class="post">
                        <header>
                            <h1 class="post-title">OAuth2.0认证授权单点登录</h1>
                        </header>
                        <date class="post-meta meta-date">
                            2020年4月18日
                        </date>
                        
                        <div class="post-meta">
                            <span>|</span>
                            
                                <span class="meta-category"><a href="http://linxianqin.gitee.io/categories/java">java</a></span>
                            
                                <span class="meta-category"><a href="http://linxianqin.gitee.io/categories/JWT">JWT</a></span>
                            
                        </div>
                        
                        
                        <div class="post-meta">
                            <span id="busuanzi_container_page_pv">|<span id="busuanzi_value_page_pv"></span><span> 阅读</span></span>
                        </div>
                        
                        
                        <div class="post-content">
							<nav id="TableOfContents">
<ul>
<li><a href="#oauth2-0认证授权">OAuth2.0认证授权</a>
<ul>
<li><a href="#1-基本概念">1、基本概念</a>
<ul>
<li><a href="#1-1什么是认证">1.1什么是认证？</a></li>
<li><a href="#1-2-什么是会话">1.2 什么是会话?</a></li>
<li><a href="#1-3-什么是授权">1.3 <strong>什么是授权</strong></a></li>
<li><a href="#1-4-授权的数据模型">1.4 <strong>授权的数据模型</strong></a>
<ul>
<li>
<ul>
<li><a href="#主体-资源-权限的关系如图">主体，资源，权限的关系如图：</a></li>
<li><a href="#主体-用户-资源-权限关系如下图">主体（用户）、资源、权限关系如下图：</a></li>
<li><a href="#修改后数据模型之间的关系如下图">修改后数据模型之间的关系如下图：</a></li>
</ul></li>
</ul></li>
<li><a href="#1-5-rbac">1.5 RBAC</a>
<ul>
<li><a href="#1-5-1-基于角色的访问控制">1.5.1 基于角色的访问控制</a></li>
<li><a href="#1-5-2-基于资源的访问控制">1.5.2 基于资源的访问控制</a></li>
</ul></li>
</ul></li>
</ul></li>
<li><a href="#二-使用jwt进行跨域身份验证">二、使用JWT进行跨域身份验证</a>
<ul>
<li><a href="#1-传统用户身份验证">1、传统用户身份验证</a></li>
<li><a href="#2-解决方案">2、解决方案</a></li>
<li><a href="#3-jwt令牌">3、JWT令牌</a>
<ul>
<li><a href="#3-1-jwt的组成">3.1、JWT的组成</a></li>
<li><a href="#3-2-jwt的原则">3.2、JWT的原则</a></li>
<li><a href="#3-3-jwt的用法">3.3、JWT的用法</a></li>
<li><a href="#3-4-jwt问题和趋势">3.4、JWT问题和趋势</a></li>
</ul></li>
</ul></li>
<li><a href="#三-整合jwt令牌完成用户登录">三、整合JWT令牌完成用户登录</a>
<ul>
<li>
<ul>
<li><a href="#1-添加jwt工具依赖">1、添加jwt工具依赖</a></li>
<li><a href="#2-创建jwt工具类">2、创建JWT工具类</a></li>
<li><a href="#3-创建loginvo用于数据封装">3、创建LoginVo用于数据封装</a></li>
<li><a href="#4-创建controller编写登录方法">4、创建controller编写登录方法</a></li>
<li><a href="#5-创建service接口和实现类">5、创建service接口和实现类</a></li>
<li><a href="#6-创建接口根据token获取用户信息">6、创建接口根据token获取用户信息</a>
<ul>
<li>
<ul>
<li>
<ul>
<li><a href="#在userapicontroller中创建方法">在UserApiController中创建方法</a></li>
<li><a href="#创建service">创建service</a></li>
</ul></li>
</ul></li>
</ul></li>
</ul></li>
</ul></li>
</ul>
</nav>
                            

<h1 id="oauth2-0认证授权">OAuth2.0认证授权</h1>

<h2 id="1-基本概念">1、基本概念</h2>

<h3 id="1-1什么是认证">1.1什么是认证？</h3>

<p>进入移动互联网时代，大家每天都在刷手机，常用的软件有微信、支付宝、头条等，下边拿微信来举例子说明认证 相关的基本概念，在初次使用微信前需要注册成为微信用户，然后输入账号和密码即可登录微信，输入账号和密码 登录微信的过程就是认证。</p>

<p>系统为什么要认证？</p>

<p>认证是为了保护系统的隐私数据与资源，用户的身份合法方可访问该系统的资源。</p>

<pre><code>认证 ：用户认证就是判断一个用户的身份是否合法的过程，用户去访问系统资源时系统要求验证用户的身份信 息，身份合法方可继续访问，不合法则拒绝访问。常见的用户身份认证方式有：用户名密码登录，二维码登录，手 机短信登录，指纹认证等方式。
</code></pre>

<h3 id="1-2-什么是会话">1.2 什么是会话?</h3>

<p>用户认证通过后，为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前 用户的登录状态所提供的机制，常见的有基于session方式、基于token方式等。</p>

<p>基于session的认证方式如下图：</p>

<p>它的交互流程是，用户认证成功后，在服务端生成用户相关的数据保存在session(当前会话)中，发给客户端的 sesssion_id 存放到 cookie 中，这样用户客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数 据，以此完成用户的合法校验，当用户退出系统或session过期销毁时,客户端的session_id也就无效了。</p>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/springcloud/session%E7%99%BB%E5%BD%95.jpg" alt="session登录" /></p>

<p>基于token方式如下图：</p>

<p>它的交互流程是，用户认证成功后，服务端生成一个token发给客户端，客户端可以放到 cookie 或 localStorage 等存储中，每次请求时带上 token，服务端收到token通过验证后即可确认用户身份。</p>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/springcloud/token%E8%AE%A4%E8%AF%81.jpg" alt="token登录" /></p>

<p>业务流程图</p>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/springcloud/token%E4%B8%9A%E5%8A%A1%E6%B5%81%E7%A8%8B%E5%9B%BE.jpg" alt="token业务流程" /></p>

<p>session与token的比较</p>

<p>session：</p>

<pre><code>优点 ： 
    用户身份信息独立管理，更好的分布式管理。
    可以自己扩展安全策略
缺点：
     认证服务器访问压力较大。
</code></pre>

<p>token</p>

<pre><code>优点：
无状态： token无状态，session有状态的
基于标准化: 你的API可以采用标准化的 JSON Web Token (JWT)
缺点：
占用带宽
无法在服务器端销毁
注：基于微服务开发，选择token的形式相对较多
</code></pre>

<p>基于session的认证方式由Servlet规范定制，服务端要存储session信息需要占用内存资源，客户端需要支持</p>

<p>cookie；基于token的方式则一般不需要服务端存储token，并且不限制客户端的存储方式。如今移动互联网时代</p>

<p>更多类型的客户端需要接入系统，系统多是采用前后端分离的架构进行实现，所以基于token的方式更适合。</p>

<h3 id="1-3-什么是授权">1.3 <strong>什么是授权</strong></h3>

<p>还拿微信来举例子，微信登录成功后用户即可使用微信的功能，比如，发红包、发朋友圈、添加好友等，没有绑定</p>

<p>银行卡的用户是无法发送红包的，绑定银行卡的用户才可以发红包，发红包功能、发朋友圈功能都是微信的资源即</p>

<p>功能资源，用户拥有发红包功能的权限才可以正常使用发送红包功能，拥有发朋友圈功能的权限才可以使用发朋友</p>

<p>圈功能，这个根据用户的权限来控制用户使用资源的过程就是授权。</p>

<p>为什么要授权？</p>

<p>认证是为了保证用户身份的合法性，授权则是为了更细粒度的对隐私数据进行划分，授权是在认证通过后发生的，</p>

<p>控制不同的用户能够访问不同的资源。</p>

<p><strong>授权</strong>： 授权是用户认证通过根据用户的权限来控制用户访问资源的过程，拥有资源的访问权限则正常访问，没有</p>

<p>权限则拒绝访问。</p>

<h3 id="1-4-授权的数据模型">1.4 <strong>授权的数据模型</strong></h3>

<p>如何进行授权即如何对用户访问资源进行控制，首先需要学习授权相关的数据模型。 授权可简单理解为Who对What(which)进行How操作，包括如下： Who，即主体（Subject），主体一般是指用户，也可以是程序，需要访问系统中的资源。 What，即资源 （Resource），如系统菜单、页面、按钮、代码方法、系统商品信息、系统订单信息等。系统菜单、页面、按 钮、代码方法都属于系统功能资源，对于web系统每个功能资源通常对应一个URL；系统商品信息、系统订单信息 都属于实体资源（数据资源），实体资源由资源类型和资源实例组成，比如商品信息为资源类型，商品编号 为001 的商品为资源实例。 How，权限/许可（Permission），规定了用户对资源的操作许可，权限离开资源没有意义， 如用户查询权限、用户添加权限、某个代码方法的调用权限、编号为001的用户的修改权限等，通过权限可知用户 对哪些资源都有哪些操作许可。</p>

<h5 id="主体-资源-权限的关系如图">主体，资源，权限的关系如图：</h5>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/RBAC/1587221433%281%29.jpg" alt="" /></p>

<p>主体、资源、权限相关的数据模型如下：</p>

<p>主体（用户id、账号、密码、&hellip;）</p>

<p>资源（资源id、资源名称、访问地址、&hellip;）</p>

<p>权限（权限id、权限标识、权限名称、资源id、&hellip;）</p>

<p>角色（角色id、角色名称、&hellip;）</p>

<p>角色和权限关系（角色id、权限id、&hellip;）</p>

<p>主体（用户）和角色关系（用户id、角色id、&hellip;）</p>

<h5 id="主体-用户-资源-权限关系如下图">主体（用户）、资源、权限关系如下图：</h5>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/RBAC/1587221581%281%29.jpg" alt="" /></p>

<p>通常企业开发中将资源和权限表合并为一张权限表，如下：</p>

<p>资源（资源id、资源名称、访问地址、&hellip;）</p>

<p>权限（权限id、权限标识、权限名称、资源id、&hellip;）</p>

<p>合并为：</p>

<p>权限（权限id、权限标识、权限名称、资源名称、资源访问地址、&hellip;）</p>

<h5 id="修改后数据模型之间的关系如下图">修改后数据模型之间的关系如下图：</h5>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/RBAC/1587221664%281%29.jpg" alt="" /></p>

<h3 id="1-5-rbac">1.5 RBAC</h3>

<p>如何实现授权？业界通常基于RBAC实现授权。</p>

<h4 id="1-5-1-基于角色的访问控制">1.5.1 基于角色的访问控制</h4>

<p>RBAC基于角色的访问控制（Role-Based Access Control）是按角色进行授权，比如：主体的角色为总经理可以查 询企业运营报表，查询员工工资信息等，访问控制流程如下：</p>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/RBAC/1587221819%281%29.jpg" alt="" /></p>

<h4 id="1-5-2-基于资源的访问控制">1.5.2 基于资源的访问控制</h4>

<p>RBAC基于资源的访问控制（Resource-Based Access Control）是按资源（或权限）进行授权，比如：用户必须</p>

<p>具有查询工资权限才可以查询员工工资信息等，访问控制流程如下：</p>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/RBAC/1587221921%281%29.jpg" alt="" /></p>

<h1 id="二-使用jwt进行跨域身份验证">二、使用JWT进行跨域身份验证</h1>

<h2 id="1-传统用户身份验证">1、传统用户身份验证</h2>

<p>Internet服务无法与用户身份验证分开。一般过程如下：</p>

<ol>
<li>用户向服务器发送用户名和密码。</li>
<li>验证服务器后，相关数据（如用户角色，登录时间等）将保存在当前会话中。</li>
<li>服务器向用户返回session_id，session信息都会写入到用户的Cookie。</li>
<li>用户的每个后续请求都将通过在Cookie中取出session_id传给服务器。</li>
<li>服务器收到session_id并对比之前保存的数据，确认用户的身份。</li>
</ol>

<p>这种模式最大的问题是，没有分布式架构，无法支持横向扩展。</p>

<h2 id="2-解决方案">2、解决方案</h2>

<ol>
<li>session广播</li>
<li>将透明令牌存入cookie，将用户身份信息存入redis</li>
</ol>

<p><strong>另外一种灵活的解决方案：</strong></p>

<p>使用自包含令牌，通过客户端保存数据，而服务器不保存会话数据。 JWT是这种解决方案的代表。</p>

<h2 id="3-jwt令牌">3、JWT令牌</h2>

<h3 id="3-1-jwt的组成">3.1、JWT的组成</h3>

<p>典型的，一个JWT看起来如下图：</p>

<p><img src="https://linxianqin.oss-cn-shenzhen.aliyuncs.com/hugo/java/RBAC/1587222247%281%29.jpg" alt="" /></p>

<p>该对象为一个很长的字符串，字符之间通过&rdquo;.&ldquo;分隔符分为三个子串。</p>

<p>每一个子串表示了一个功能块，总共有以下三个部分：JWT头、有效载荷和签名</p>

<p><strong>JWT头</strong></p>

<p>JWT头部分是一个描述JWT元数据的JSON对象，通常如下所示。</p>

<pre><code class="language-json">{
  &quot;alg&quot;: &quot;HS256&quot;,
  &quot;typ&quot;: &quot;JWT&quot;
}
</code></pre>

<p>在上面的代码中，alg属性表示签名使用的算法，默认为HMAC SHA256（写为HS256）；typ属性表示令牌的类型，JWT令牌统一写为JWT。最后，使用Base64 URL算法将上述JSON对象转换为字符串保存。</p>

<p><strong>有效载荷</strong></p>

<p>有效载荷部分，是JWT的主体内容部分，也是一个JSON对象，包含需要传递的数据。 JWT指定七个默认字段供选择。</p>

<pre><code>iss：发行人
exp：到期时间
sub：主题
aud：用户
nbf：在此之前不可用
iat：发布时间
jti：JWT ID用于标识该JWT
</code></pre>

<p>除以上默认字段外，我们还可以自定义私有字段，如下例：</p>

<pre><code>{
  &quot;sub&quot;: &quot;1234567890&quot;,
  &quot;name&quot;: &quot;Helen&quot;,
  &quot;admin&quot;: true
}
</code></pre>

<p>请注意，默认情况下JWT是未加密的，任何人都可以解读其内容，因此不要构建隐私信息字段，存放保密信息，以防止信息泄露。JSON对象也使用Base64 URL算法转换为字符串保存。</p>

<p><strong>签名哈希</strong></p>

<p>签名哈希部分是对上面两部分数据签名，通过指定的算法生成哈希，以确保数据不会被篡改。</p>

<p>首先，需要指定一个密码（secret）。该密码仅仅为保存在服务器中，并且不能向用户公开。然后，使用标头中指定的签名算法（默认情况下为HMAC SHA256）根据以下公式生成签名。</p>

<pre><code>HMACSHA256(base64UrlEncode(header) + &quot;.&quot; + base64UrlEncode(claims), secret)
</code></pre>

<p>在计算出签名哈希后，JWT头，有效载荷和签名哈希的三个部分组合成一个字符串，每个部分用&rdquo;.&ldquo;分隔，就构成整个JWT对象。</p>

<p><strong>Base64URL算法</strong></p>

<p>如前所述，JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似，稍有差别。</p>

<p>作为令牌的JWT可以放在URL中（例如api.example/?token=xxx）。 Base64中用的三个字符是&rdquo;+&ldquo;，&rdquo;/&ldquo;和&rdquo;=&ldquo;，由于在URL中有特殊含义，因此Base64URL中对他们做了替换：&rdquo;=&ldquo;去掉，&rdquo;+&ldquo;用&rdquo;-&ldquo;替换，&rdquo;/&ldquo;用&rdquo;_&ldquo;替换，这就是Base64URL算法。</p>

<h3 id="3-2-jwt的原则">3.2、JWT的原则</h3>

<p>JWT的原则是在服务器身份验证之后，将生成一个JSON对象并将其发送回用户，如下所示。</p>

<pre><code>{
  &quot;sub&quot;: &quot;1234567890&quot;,
  &quot;name&quot;: &quot;Helen&quot;,
  &quot;admin&quot;: true
}
</code></pre>

<p>之后，当用户与服务器通信时，客户在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据，服务器将在生成对象时添加签名。服务器不保存任何会话数据，即服务器变为无状态，使其更容易扩展。</p>

<h3 id="3-3-jwt的用法">3.3、JWT的用法</h3>

<p>客户端接收服务器返回的JWT，将其存储在Cookie或localStorage中。</p>

<p>此后，客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中，就可以自动发送，但是不会跨域，因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时，也可以将JWT被放置于POST请求的数据主体中。</p>

<h3 id="3-4-jwt问题和趋势">3.4、JWT问题和趋势</h3>

<ul>
<li>JWT不仅可用于认证，还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。</li>
<li>生产的token可以包含基本信息，比如id、用户昵称、头像等信息，避免再次查库</li>

<li><p>存储在客户端，不占用服务端的内存资源</p></li>

<li><p>JWT默认不加密，但可以加密。生成原始令牌后，可以再次对其进行加密。</p></li>

<li><p>当JWT未加密时，一些私密数据无法通过JWT传输。</p></li>

<li><p>JWT的最大缺点是服务器不保存会话状态，所以在使用期间不可能取消令牌或更改令牌的权限。也就是说，一旦JWT签发，在有效期内将会一直有效。</p></li>

<li><p>JWT本身包含认证信息，token是经过base64编码，所以可以解码，因此token加密前的对象不应该包含敏感信息，一旦信息泄露，任何人都可以获得令牌的所有权限。为了减少盗用，JWT的有效期不宜设置太长。对于某些重要操作，用户在使用时应该每次都进行进行身份验证。</p></li>

<li><p>为了减少盗用和窃取，JWT不建议使用HTTP协议来传输代码，而是使用加密的HTTPS协议进行传输。</p></li>
</ul>

<hr />

<h1 id="三-整合jwt令牌完成用户登录">三、整合JWT令牌完成用户登录</h1>

<h3 id="1-添加jwt工具依赖">1、添加jwt工具依赖</h3>

<pre><code class="language-xml">&lt;dependencies&gt;
    &lt;!-- JWT--&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;io.jsonwebtoken&lt;/groupId&gt;
        &lt;artifactId&gt;jjwt&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>

<h3 id="2-创建jwt工具类">2、创建JWT工具类</h3>

<pre><code class="language-xml">import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

public class JwtUtils {

    public static final long EXPIRE = 1000 * 60 * 60 * 24;

    public static final String APP_SECRET = &quot;ukc8BDbRigUDaY6pZFfWus2jZWLPHO&quot;;

    public static String getJwtToken(String id, String nickname){
        String JwtToken = Jwts.builder()
                .setHeaderParam(&quot;typ&quot;, &quot;JWT&quot;)
                .setHeaderParam(&quot;alg&quot;, &quot;HS256&quot;)
                .setSubject(&quot;guli-user&quot;)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .claim(&quot;id&quot;, id)
                .claim(&quot;nickname&quot;, nickname)
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();
        return JwtToken;
    }
    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader(&quot;token&quot;);
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 根据token获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader(&quot;token&quot;);
        if(StringUtils.isEmpty(jwtToken)) return &quot;&quot;;
        Jws&lt;Claims&gt; claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get(&quot;id&quot;);
    }
}
</code></pre>

<h3 id="3-创建loginvo用于数据封装">3、创建LoginVo用于数据封装</h3>

<pre><code class="language-xml">@Data
@ApiModel(value=&quot;登录对象&quot;, description=&quot;登录对象&quot;)
public class LoginVo {

    @ApiModelProperty(value = &quot;用户名&quot;)
    private String username;

    @ApiModelProperty(value = &quot;密码&quot;)
    private String password;

}
</code></pre>

<h3 id="4-创建controller编写登录方法">4、创建controller编写登录方法</h3>

<pre><code class="language-xml">
    @ApiOperation(value = &quot;会员登录&quot;)
    @PostMapping(&quot;login&quot;)
    public R login(@RequestBody LoginVo loginVo) {
        String token = userService.login(loginVo);
        return R.ok().data(&quot;token&quot;, token);
    }
</code></pre>

<h3 id="5-创建service接口和实现类">5、创建service接口和实现类</h3>

<pre><code class="language-xml">/**
     * 会员登录
     * @param loginVo
     * @return
     */
    @Override
    public String login(LoginVo loginVo) {
        String username = loginVo.getUserName();
        String password = loginVo.getPassword();
        //校验参数
        if(StringUtils.isEmpty(username) ||
                StringUtils.isEmpty(password) ||
                StringUtils.isEmpty(mobile)) {
			// BaseException是封装好的全局异常
            throw new BaseException(20001,&quot;用户或密码不能为空&quot;);
        }
        //获取会员：这里用的是mybatisPlus查询数据库
        User user = baseMapper.selectOne(new QueryWrapper&lt;User&gt;().eq(&quot;username&quot;, username));
        if(null == user) {
            throw new BaseException(20001,&quot;用户名不存在&quot;);
        }
        //校验密码
        if(!MD5.encrypt(password).equals(member.getPassword())) {
            throw new BaseException(20001,&quot;密码不正确&quot;);
        }
        //校验是否被禁用
        if(user.getIsDisabled()) {
            throw new BaseException(20001,&quot;账户已被禁用&quot;);
        }
        //使用JWT生成token字符串
        String token = JwtUtils.getJwtToken(user.getId(), user.getNickname());
        return token;
    }
</code></pre>

<h3 id="6-创建接口根据token获取用户信息">6、创建接口根据token获取用户信息</h3>

<h6 id="在userapicontroller中创建方法">在UserApiController中创建方法</h6>

<pre><code class="language-java">@ApiOperation(value = &quot;根据token获取登录信息&quot;)
@GetMapping(&quot;auth/getLoginInfo&quot;)
public R getLoginInfo(HttpServletRequest request){
    try {
        String userId = JwtUtils.getUserIdByJwtToken(request);
        LoginInfoVo loginInfoVo = userService.getLoginInfo(userId);
        return R.ok().data(&quot;item&quot;, loginInfoVo);
    }catch (Exception e){
        e.printStackTrace();
        throw new BaseException(20001,&quot;error&quot;);
    }
}
</code></pre>

<h6 id="创建service">创建service</h6>

<pre><code class="language-java">@Override
public LoginInfoVo getLoginInfo(String userId) {
    User user = baseMapper.selectById(userId);
    LoginInfoVo loginInfoVo = new LoginInfoVo();
    BeanUtils.copyProperties(user, loginInfoVo);
    return loginInfoVo;
}
</code></pre>

                        </div>
						 
                        
<div class="post-archive">
    <ul class="post-copyright">
        <li><strong>原文作者：</strong><a rel="author" href="http://linxianqin.gitee.io">林贤钦</a></li>
        <li style="word-break:break-all"><strong>原文链接：</strong><a href="http://linxianqin.gitee.io/post/OAuth2.0%E8%AE%A4%E8%AF%81%E6%8E%88%E6%9D%83%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95/">http://linxianqin.gitee.io/post/OAuth2.0%E8%AE%A4%E8%AF%81%E6%8E%88%E6%9D%83%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95/</a></li>
        <li><strong>版权声明：</strong>本作品采用<a rel="license" href="https://linxianqin.gitee.io">知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议</a>进行许可，非商业转载请注明出处（作者，原文链接），商业转载请联系作者获得授权。</li>
    </ul>
</div>
<br/>



                        

<div class="post-archive">
    <h2>See Also</h2>
    <ul class="listing">
        
        <li><a href="/post/%E9%98%BF%E9%87%8C%E4%BA%91%E8%A7%86%E9%A2%91%E7%82%B9%E6%92%AD%E6%9C%8D%E5%8A%A1/">阿里云视频点播服务前后端实现</a></li>
        
        <li><a href="/post/%E9%98%BF%E9%87%8C%E4%BA%91EasyExcel%E5%AE%9E%E7%8E%B0%E5%90%8E%E7%AB%AF%E5%AF%B9Excel%E7%9A%84%E8%AF%BB%E5%86%99%E6%93%8D%E4%BD%9C/">使用阿里云EasyExcel实现后端对Excel的读写操作</a></li>
        
        <li><a href="/post/%E9%98%BF%E9%87%8C%E4%BA%91oss%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1%E5%AE%9E%E7%8E%B0/">阿里云oss对象存储服务实现</a></li>
        
        <li><a href="/post/Nacos%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/">Nacos服务注册中心</a></li>
        
        <li><a href="/post/SpringCloud%E5%BE%AE%E6%9C%8D%E5%8A%A1/">SpringCloud微服务</a></li>
        
    </ul>
</div>


                        <div class="post-meta meta-tags">
                            
                            <ul class="clearfix">
                                
                                <li><a href="http://linxianqin.gitee.io/tags/%E9%98%BF%E9%87%8C%E4%BA%91">阿里云</a></li>
                                
                                <li><a href="http://linxianqin.gitee.io/tags/java">java</a></li>
                                
                                <li><a href="http://linxianqin.gitee.io/tags/JWT">JWT</a></li>
                                
                            </ul>
                            
                        </div>
                    </article>
                    
    

    
    
    <div class="post bg-white">
      <script src="https://utteranc.es/client.js"
            repo= "757610938/hugo-blogs-utterances"
            issue-term="pathname"
            theme="github-light"
            crossorigin="anonymous"
            async>
      </script>
    </div>
    
                </div>
            </div>
            <div id="secondary">
    <section class="widget">
        <form id="search" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank" _lpchecked="1">
      
      <input type="text" name="q" maxlength="20" placeholder="Search">
      <input type="hidden" name="sitesearch" value="http://linxianqin.gitee.io">
      <button type="submit" class="submit icon-search"></button>
</form>
    </section>
    
    <section class="widget">
        <h3 class="widget-title">最近文章</h3>
<ul class="widget-list">
    
    <li>
        <a href="http://linxianqin.gitee.io/post/java-%E5%8F%8D%E5%B0%84/" title="Java 反射">Java 反射</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/java-%E9%93%BE%E8%A1%A8/" title="Java 链表">Java 链表</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/%E7%9F%AD%E4%BF%A1%E6%B3%A8%E5%86%8C%E5%92%8C%E5%BE%AE%E4%BF%A1%E7%99%BB%E5%BD%95%E7%9A%84%E5%AE%9E%E7%8E%B0/" title="短信注册和微信登录的实现">短信注册和微信登录的实现</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/OAuth2.0%E8%AE%A4%E8%AF%81%E6%8E%88%E6%9D%83%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95/" title="OAuth2.0认证授权单点登录">OAuth2.0认证授权单点登录</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/Nacos%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83/" title="Nacos服务注册中心">Nacos服务注册中心</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/SpringCloud%E5%BE%AE%E6%9C%8D%E5%8A%A1/" title="SpringCloud微服务">SpringCloud微服务</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/%E9%98%BF%E9%87%8C%E4%BA%91%E8%A7%86%E9%A2%91%E7%82%B9%E6%92%AD%E6%9C%8D%E5%8A%A1/" title="阿里云视频点播服务前后端实现">阿里云视频点播服务前后端实现</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/%E9%98%9F%E5%88%97/" title="队列">队列</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/%E7%A8%80%E7%96%8F%E6%95%B0%E7%BB%84/" title="稀疏数组">稀疏数组</a>
    </li>
    
    <li>
        <a href="http://linxianqin.gitee.io/post/%E9%98%BF%E9%87%8C%E4%BA%91EasyExcel%E5%AE%9E%E7%8E%B0%E5%90%8E%E7%AB%AF%E5%AF%B9Excel%E7%9A%84%E8%AF%BB%E5%86%99%E6%93%8D%E4%BD%9C/" title="使用阿里云EasyExcel实现后端对Excel的读写操作">使用阿里云EasyExcel实现后端对Excel的读写操作</a>
    </li>
    
</ul>
    </section>

    

    <section class="widget">
        <h3 class="widget-title">分类</h3>
<ul class="widget-list">
    
    <li><a href="http://linxianqin.gitee.io/categories/JWT/">JWT (2)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/hugo/">hugo (2)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/java/">java (18)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/java8/">java8 (1)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/springCloud/">springCloud (2)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/springSecurity/">springSecurity (4)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/vue/">vue (5)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/yml/">yml (1)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">数据结构 (3)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/">软件测试 (1)</a></li>
    
    <li><a href="http://linxianqin.gitee.io/categories/%E9%98%BF%E9%87%8C%E4%BA%91/">阿里云 (4)</a></li>
    
</ul>
    </section>

    <section class="widget">
        <h3 class="widget-title">标签</h3>
<div class="tagcloud">
    
    <a href="http://linxianqin.gitee.io/tags/JWT/">JWT</a>
    
    <a href="http://linxianqin.gitee.io/tags/hugo/">hugo</a>
    
    <a href="http://linxianqin.gitee.io/tags/java/">java</a>
    
    <a href="http://linxianqin.gitee.io/tags/java8/">java8</a>
    
    <a href="http://linxianqin.gitee.io/tags/spring/">spring</a>
    
    <a href="http://linxianqin.gitee.io/tags/springBoot/">springBoot</a>
    
    <a href="http://linxianqin.gitee.io/tags/springCloud/">springCloud</a>
    
    <a href="http://linxianqin.gitee.io/tags/ssm/">ssm</a>
    
    <a href="http://linxianqin.gitee.io/tags/vue/">vue</a>
    
    <a href="http://linxianqin.gitee.io/tags/yml/">yml</a>
    
    <a href="http://linxianqin.gitee.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">数据结构</a>
    
    <a href="http://linxianqin.gitee.io/tags/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/">软件测试</a>
    
    <a href="http://linxianqin.gitee.io/tags/%E9%98%BF%E9%87%8C%E4%BA%91/">阿里云</a>
    
</div>
    </section>

    
<section class="widget">
    <h3 class="widget-title">友情链接</h3>
    <ul class="widget-list">
        
        <li>
            <a target="_blank" href="https://www.cnki.net" title="中国知网">中国知网</a>
        </li>
        
        <li>
            <a target="_blank" href="https://linxianqin.com" title="个人的另一个博客网站(国内加载慢)">个人的另一个博客网站(国内加载慢)</a>
        </li>
        
    </ul>
</section>


    <section class="widget">
        <h3 class="widget-title">其它</h3>
        <ul class="widget-list">
            <li><a href="http://linxianqin.gitee.io/index.xml">文章 RSS</a></li>
        </ul>
    </section>
</div>
        </div>
    </div>
</div>
<footer id="footer">
    <div class="container">
        &copy; 2020 <a href="http://linxianqin.gitee.io">林贤钦 By 林贤钦</a>.
        Powered by <a rel="nofollow noreferer noopener" href="https://gohugo.io" target="_blank">Hugo</a>.
        <a href="https://www.flysnow.org/" target="_blank">Theme</a> based on <a href="https://github.com/rujews/maupassant-hugo" target="_blank">maupassant</a>.
        
    </div>
</footer>


    
    <script type="text/javascript">
        
        (function () {
            $("pre code").parent().addClass("line-numbers")
        }());

        window.MathJax = {
            tex2jax: {
                inlineMath: [['$', '$']],
                processEscapes: true
                }
            };
    </script>
    <script type="text/javascript" src="/js/prism.js" async="true"></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script>


<a id="rocket" href="#top"></a>
<script type="text/javascript" src="/js/totop.js?v=0.0.0" async=""></script>

<script type="application/javascript">
var doNotTrack = false;
if (!doNotTrack) {
	window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
	ga('create', 'GA ID', 'auto');
	
	ga('send', 'pageview');
}
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>



    <script type="text/javascript" src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>




</body>
</html>
